package com.benjaminwan.read.free.ui.net

import android.content.Context
import androidx.paging.DataSource
import androidx.paging.LivePagedListBuilder
import androidx.paging.PageKeyedDataSource
import androidx.paging.PagedList
import com.airbnb.epoxy.paging.PagedListEpoxyController
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
import com.benjaminwan.read.free.base.viewmodel.MvRxViewModel
import com.benjaminwan.read.free.base.viewmodel.state
import com.benjaminwan.read.free.common.*
import com.benjaminwan.read.free.model.bean.DetailBean
import com.benjaminwan.read.free.model.bean.DetailListBean
import com.benjaminwan.read.free.net.RetrofitFactory
import com.benjaminwan.read.free.net.service.HiPDAService
import com.benjaminwan.read.free.utils.moshiAny
import com.orhanobut.logger.Logger
import io.reactivex.Observable
import io.reactivex.schedulers.Schedulers

class NetViewModel(
    initialState: NetState,
    val context: Context
) : MvRxViewModel<NetState>(initialState) {

    private var spUserName: String by context.appPrefs.moshiAny(SP_USERNAME, "")
    private var spPassword: String by context.appPrefs.moshiAny(SP_PASSWORD, "")
    private var spQuestionId: Int by context.appPrefs.moshiAny(SP_QUESTION, 0)
    private var spAnswer: String by context.appPrefs.moshiAny(SP_ANSWER, "")

    var initTid: String = DefaultTid
    var initPage: Int = 1

    private val dataSource = DetailDataSource(this)

    private val factory: DataSource.Factory<Int, DetailBean> =
        object : DataSource.Factory<Int, DetailBean>() {
            override fun create(): DataSource<Int, DetailBean> {
                return dataSource
            }
        }

    private val pagedListConfig = PagedList.Config.Builder()
        .setInitialLoadSizeHint(50)//设置首次加载的数量
        .setPageSize(50)//设置每一页加载的数量
        .setPrefetchDistance(3)//设置距离最后还有多少个item时，即寻呼库开始加载下一页的数据
        .setEnablePlaceholders(false)//表示是否设置null占位符
        .build()

    var pagedList = LivePagedListBuilder(factory, pagedListConfig).build()

    init {
        setState {
            copy(
                username = spUserName, password = spPassword,
                questionId = spQuestionId, answer = spAnswer
            )
        }
    }

    fun clearLoginInfo() {
        spUserName = ""
        spPassword = ""
        spQuestionId = 0
        spAnswer = ""
        setState { copy(username = "", password = "", questionId = 0, answer = "") }
    }

    fun setUserName(name: String) {
        spUserName = name
        setState { copy(username = name) }
    }

    fun setPassword(password: String) {
        spPassword = password
        setState { copy(password = password) }
    }

    fun setQuestionId(questionId: Int) {
        spQuestionId = questionId
        setState { copy(questionId = questionId) }
    }

    fun setAnswer(answer: String) {
        spAnswer = answer
        setState { copy(answer = answer) }
    }

    fun resetLoginRequest() {
        setState {
            copy(loginRequest = Uninitialized)
        }
    }

    fun resetThreadRequest() {
        setState {
            copy(threadRequest = Uninitialized)
        }
    }

    fun login() {
        RetrofitFactory.clearCookies()
        RetrofitFactory.clearLoginResult()
        HiPDAService.getFormHash()
            .subscribeOn(Schedulers.io())
            .observeOn(Schedulers.io())
            .flatMap {
                if (it.state == com.benjaminwan.read.free.model.enums.NetRetState.SUCCESS) {
                    val state = this.state
                    val username = state.username
                    val password = state.password
                    val questionId = state.questionId.toString()
                    val answer = state.answer
                    HiPDAService.postLogin(it.msg, username, password, questionId, answer)
                } else {
                    Observable.error(Throwable(it.msg))
                }
            }
            .execute {
                copy(loginRequest = it)
            }
    }

    fun loginAndGetThread(tid: String, page: Int) {
        HiPDAService.getFormHash()
            .subscribeOn(Schedulers.io())
            .observeOn(Schedulers.io())
            .flatMap {
                if (it.state == com.benjaminwan.read.free.model.enums.NetRetState.SUCCESS) {
                    val state = this.state
                    val username = state.username
                    val password = state.password
                    val questionId = state.questionId.toString()
                    val answer = state.answer
                    HiPDAService.postLogin(it.msg, username, password, questionId, answer)
                } else {
                    Observable.error(Throwable(it.msg))
                }
            }.flatMap {
                if (it.state == com.benjaminwan.read.free.model.enums.NetRetState.SUCCESS) {
                    HiPDAService.getThread(tid, page)
                } else {
                    Observable.error(Throwable(it.msg))
                }
            }
            .execute {
                copy(threadRequest = it)
            }
    }

    /*fun getPM() {
        HiPDAService.getPM()
            .subscribeOn(Schedulers.io())
            .observeOn(Schedulers.io())
            .execute {
                copy(pmRequest = it)
            }
    }

    fun getForm(fid: String, page: String) {
        HiPDAService.getForum(fid, page)
            .subscribeOn(Schedulers.io())
            .observeOn(Schedulers.io())
            .execute {
                copy(forumRequest = it)
            }
    }

    fun getCDN() {
        HiPDAService.getCDN()
            .subscribeOn(Schedulers.io())
            .observeOn(Schedulers.io())
            .execute {
                copy(cdnRequest = it)
            }
    }*/

    fun getThread(tid: String, page: Int) {
        HiPDAService.getThread(tid, page)
            .subscribeOn(Schedulers.io())
            .observeOn(Schedulers.io())
            .execute {
                copy(threadRequest = it)
            }
    }

    fun getLastVersion() {
        HiPDAService.getVersion()
            .subscribeOn(Schedulers.io())
            .observeOn(Schedulers.io())
            .execute {
                copy(versionRequest = it)
            }
    }

    fun resetVersionRequest() {
        setState { copy(versionRequest = Uninitialized) }
    }

    fun getThreadInit(
        tid: String, page: Int,
        params: PageKeyedDataSource.LoadInitialParams<Int>,
        callback: PageKeyedDataSource.LoadInitialCallback<Int, DetailBean>
    ) {
        HiPDAService.getThread(tid, page)
            .subscribeOn(Schedulers.io())
            .observeOn(Schedulers.io())
            .map {
                val detailList = it.detail
                if (detailList != null) {
                    val curPage = detailList.page
                    val nextPage =
                        if (curPage + 1 <= detailList.lastPage) curPage + 1 else detailList.lastPage
                    val data = detailList.all
                    Logger.i("loadSize=${params.requestedLoadSize},curPage=$curPage, lastPage = ${detailList.lastPage}, nextPage=$nextPage\ndata=${data.size}")
                    callback.onResult(data, curPage, curPage + 1)
                } else {
                    callback.onResult(emptyList(), 1, 2)
                }
                it
            }
            .execute {
                copy(threadRequest = it)
            }
    }

    fun getThreadAfter(
        tid: String,
        params: PageKeyedDataSource.LoadParams<Int>,
        callback: PageKeyedDataSource.LoadCallback<Int, DetailBean>
    ) {
        HiPDAService.getThread(tid, params.key)
            .subscribeOn(Schedulers.io())
            .observeOn(Schedulers.io())
            .map {
                val detailList = it.detail
                if (detailList != null) {
                    val curPage = detailList.page
                    val data =
                        if (params.key <= detailList.lastPage) detailList.all else emptyList()
                    Logger.i("key=${params.key},curPage=$curPage, lastPage = ${detailList.lastPage}\ndata=${data.size}")
                    callback.onResult(data, params.key + 1)
                } else {
                    callback.onResult(emptyList(), params.key + 1)
                }
                it
            }
            .execute {
                copy(threadRequest = it)
            }
    }

    fun getThreadBefore(
        tid: String,
        params: PageKeyedDataSource.LoadParams<Int>,
        callback: PageKeyedDataSource.LoadCallback<Int, DetailBean>
    ) {
        if (params.key >= 1) {
            HiPDAService.getThread(tid, params.key)
                .subscribeOn(Schedulers.io())
                .observeOn(Schedulers.io())
                .map {
                    val detailList = it.detail
                    if (detailList != null) {
                        val curPage = detailList.page
                        val data = detailList.all
                        Logger.i("key=${params.key},curPage=$curPage, lastPage = ${detailList.lastPage}\ndata=${data.size}")
                        callback.onResult(data, params.key - 1)
                    } else {
                        callback.onResult(emptyList(), params.key - 1)
                    }
                    it
                }
                .execute {
                    copy(threadRequest = it)
                }
        } else {
            callback.onResult(emptyList(), params.key - 1)
        }
    }

    fun setThreadDetail(detail: DetailListBean?) {
        setState {
            copy(threadDetail = detail)
        }
    }

    fun refresh(controller: PagedListEpoxyController<DetailBean>, tid: String, page: Int) {
        pagedList = LivePagedListBuilder(factory, pagedListConfig).build()
        controller.submitList(null)
    }

    fun postReply(tid: String, message: String) {
        HiPDAService.getPrePost(tid)
            .subscribeOn(Schedulers.io())
            .observeOn(Schedulers.io())
            .flatMap {
                if (it.state == com.benjaminwan.read.free.model.enums.NetRetState.SUCCESS) {
                    val formhash = it.prePostInfo?.formhash
                    if (formhash == null) {
                        Observable.error(Throwable("发表失败，无法获取必要信息 ！"))
                    } else {
                        HiPDAService.postReply(tid, formhash, message)
                    }
                } else {
                    Observable.error(Throwable(it.msg))
                }
            }.execute {
                copy(postRequest = it)
            }

    }

    override fun onCleared() {
        super.onCleared()
    }

    companion object : MvRxViewModelFactory<NetViewModel, NetState> {
        const val DefaultTid = "2772990"//"1579403"
        override fun create(
            viewModelContext: ViewModelContext,
            state: NetState
        ): NetViewModel? {
            val context = viewModelContext.activity.applicationContext
            return NetViewModel(state, context)
        }
    }
}